﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using ElectionOfLeaderAlg.Controllers;
using Kernel;

namespace ElectionOfLiderAlg
{
    public class ElectionRingPeterson : IStepController
    {
        private readonly Graph _graph;
        private int _step = 0;
        //private int _count_receive_one;
        //будет равно количеству активных процессов когда они примут сообщене two и примут решение по выборам в тройке узлов
        //private int _count_receive_two;
        private bool _end_alg;
        private List<bool> _begin_round;          

        /// <summary>
        /// Создаёт контроллер для алгоритма Ле-Лана на кольце
        /// </summary>
        /// <param name="nodeCount">общее количество вершин</param>
        /// <param name="initiatorCount">количество инициаторов</param>
        public ElectionRingPeterson(int nodeCount, int countInit, RichTextBox rtbLog)
        {
            if (countInit >= nodeCount)
                throw new Exception("Инициаторов не может быть больше чем вершин в графе");
            _graph = GraphGenerator.CreateRing(nodeCount);
            if (countInit == nodeCount)
            {
                _graph.Nodes.ForEach(x => x.IsInitiator = true);
            }
            else
            {
                Random rand = new Random();
                int colI = 0;
                int randIn = 0;
                while (colI != countInit)
                {
                    randIn = rand.Next(0, nodeCount - 1);
                    if (!_graph.Nodes[randIn].IsInitiator)
                    {
                        colI++;
                        _graph.Nodes[randIn].IsInitiator = true;
                    }
                }
            }
            _begin_round = new List<bool>(_graph.Nodes.Count);
            _graph.Nodes.ForEach(x =>
                {
                    x.rtbLog = rtbLog;
                    x.State = (x.IsInitiator)?
                        x.State = GraphNode.NodeState.Active: x.State = GraphNode.NodeState.Passive;
                    x.Ci = x.Number;
                    x.Acn = -1;
                    x.Win = -1;
                    _begin_round.Add(true);
                    x.ToolTipMessage = String.Format("Ci = {0}, Acn = undef", x.Number);
                });
            _end_alg = false;
            
        }

        public Graph NextStep()
        {
            //принимаем все сообщения, отправленные на предыдущем шаге
            _graph.Nodes.ForEach(x => x.ReceiveMessages());

            foreach (var node in _graph.Nodes)
            {
                if (node.State == GraphNode.NodeState.Active)
                {
                    if (_begin_round[node.Number])
                    {
                        //if (_count_receive_one == 0)
                        //{
                            var nextNode = node.Children.Single();
                            node.SendMessage(nextNode, new Msg(node.Ci, MessageType.one));
                            _begin_round[node.Number] = false;
                        //}
                    }
                    else
                    {
                        if (node.ReceivedMessages.Count != 0)
                        {
                            Msg tmpMsg = node.ReceivedMessages.Dequeue();
                            if (tmpMsg.mesType == MessageType.one)
                            {
                                node.Acn = tmpMsg.number;
                                node.ToolTipMessage = String.Format("Ci = {0}, Acn = {1}", node.Number, node.Acn);
                                if (node.Acn == node.Ci)
                                {
                                    node.Win = node.Acn;
                                    var nextNode2 = node.Children.Single();
                                    node.SendMessage(nextNode2, new Msg(node.Win, MessageType.small));
                                }
                                else
                                {
                                    var nextNode3 = node.Children.Single();
                                    node.SendMessage(nextNode3, new Msg(node.Acn, MessageType.two));
                                }
                            }
                            if (tmpMsg.mesType == MessageType.two)
                            {
                                if (node.Acn < node.Ci && node.Acn < tmpMsg.number)
                                    node.Ci = node.Acn;
                                else
                                    node.State = GraphNode.NodeState.Passive;
                                node.ToolTipMessage = String.Format("Ci = {0}, Acn = {1}", node.Number, node.Acn);
                                _begin_round[node.Number] = true;
                            }
                            if (tmpMsg.mesType == MessageType.small)
                            {
                                node.State = (tmpMsg.number == node.Number) ? GraphNode.NodeState.Leader : GraphNode.NodeState.Lost;
                                _end_alg = true;
                            }
                        }
                        //if(_count_receive_two == _graph.Nodes.Count(x => x.State == GraphNode.NodeState.Active
                    }
                }
                else
                {
                    //if (node.State == GraphNode.NodeState.Sleep)
                    //    node.State = GraphNode.NodeState.Lost;
                    if (node.ReceivedMessages.Count != 0)
                    {
                        Msg tmp = node.ReceivedMessages.Dequeue();
                        if (tmp.mesType == MessageType.small)
                            node.State = (tmp.number == node.Number) ? GraphNode.NodeState.Leader : GraphNode.NodeState.Lost;

                        var nextNode = node.Children.Single();
                        node.SendMessage(nextNode, tmp);
                    }
                }
            }

            _step++;

            return _graph;
        }

        public bool IsEnd()
        {
            return _end_alg;
        }

        public Graph GetCurrentState()
        {
            return _graph; 
        }
    }
}
